// Copyright (C) joyinter
// All rights reserved.
// 
// Author : Jiang Li
// Date   : 2004-8-24

#include "stdafx.h"
#include "sockbase.h"
#include "utility.h"
#include "socktool.h"

int g_sockSendThread_Priority = THREAD_PRIORITY_NORMAL;
int g_sockRecvThread_Priority = THREAD_PRIORITY_NORMAL;

//##ModelId=44CF175700C0
CJoySockSendData* CJoySockSendData::Create(int nSize)
{
    CJoySockSendData* pData = (CJoySockSendData*)new char[sizeof(CJoySockSendData)+nSize];
    JoyConstruct(pData);
    pData->m_size   = nSize;
    return pData;
}

//##ModelId=44CF175700CB
CJoySockSendData* CJoySockSendData::Create(const void* pBuf, int nSize)
{
    CJoySockSendData* pData = Create(nSize);
    memcpy(pData->GetData(), pBuf, nSize);
    return pData;
}

//##ModelId=44CF1757010C
CJoySockRecvData* CJoySockRecvData::Create(CJoySockData* sockData, int nSize)
{
    CJoySockRecvData* pData = (CJoySockRecvData*)new char[sizeof(CJoySockRecvData)+nSize];
    JoyConstruct(pData);
    pData->m_size = nSize;
    pData->m_sockData = sockData;
    sockData->AddRef();
    return pData;
}

//##ModelId=44CF1757011A
CJoySockRecvData* CJoySockRecvData::Create(CJoySockData* sockData, const void* pBuf, int nSize)
{
    CJoySockRecvData* pData = Create(sockData, nSize);
    memcpy(pData->GetData(), pBuf, nSize);
    return pData;
}

//##ModelId=44CF17570100
CJoySockRecvData::~CJoySockRecvData()
{
    if(m_sockData != NULL)
    {
        m_sockData->Release();
    }
}

// class CJoySockData

//##ModelId=44CF17570196
CJoySockData::CJoySockData()
{
    m_hSocket       = INVALID_SOCKET;
    m_hSendThread   = NULL;
    m_hRecvThread   = NULL;
    m_hSendEvent    = NULL;
    m_owner         = NULL;
    m_bClose        = false;
    m_bCancelSend   = false;
    m_bConnected    = false;
    m_bStream       = false;
    m_bBlocking     = false;
}

//##ModelId=44CF17570197
CJoySockData::~CJoySockData()
{
    if(m_hSocket == INVALID_SOCKET)
    {
        return;
    }

    if(m_hSendThread != NULL)
    {
        TerminateThread(m_hSendThread, 0);
        CloseHandle(m_hSendThread);
    }
    if(m_hRecvThread != NULL)
    {
        TerminateThread(m_hRecvThread, 0);
        CloseHandle(m_hRecvThread);
    }

    closesocket(m_hSocket);


    if(m_hSendEvent != NULL)
    {
        CloseHandle(m_hSendEvent);
    }
}

static bool TCPSend(SOCKET s, const void* pBuf, int len)
{
    char* buf = (char*)pBuf;
    int sent = 0;
    while(sent < len)
    {
        int r = send(s, buf+sent, len-sent, 0);
        if(r >= 0)
        {
            sent += r;
            continue;
        }

        int e = WSAGetLastError();
        switch(e)
        {
        case WSAETIMEDOUT:
            continue;

        case WSAEWOULDBLOCK:
            {
                int c = 0;
                do
                {
					fd_set fd;
					FD_ZERO(&fd);
					FD_SET(s,&fd);
					timeval tv;
					tv.tv_sec=0;
                    tv.tv_usec = 100;
                    c = select(0,NULL,&fd,NULL,&tv);
                }
                while(0 == c);
            }
            continue;
        default:
            return false;
        }
        break;
    }
    return true;
}

//##ModelId=44CF175701D5
bool CJoySockData::Send(CJoySockSendData* pData, JoySockBlockingMode bm)
{
    bool bBlocking;
    if(bm == SBM_Blocking)
    {
        bBlocking = true;
    }
    else if(bm == SBM_Threading)
    {
        bBlocking = false;
    }
    else
    {
        bBlocking = m_bBlocking;
    }

    if(!bBlocking)
    {
        pData->AddRef();
        CJoySockData::CSendItem* pItem = new CJoySockData::CSendItem;
        pItem->m_next = NULL;
        pItem->m_data = pData;
        PostSendItem(pItem);
        return true;
    }

    m_sendingLock.Lock();
    if(m_bBlock)
    {
        TCPSend(m_hSocket, pData->GetBlock(), pData->GetBlockSize());
    }
    else
    {
        TCPSend(m_hSocket, pData->GetData(), pData->GetDataSize());
    }
    m_sendingLock.Unlock();
    return true;
}

//##ModelId=44CF175701E4
bool CJoySockData::Send(const void* buf, int size, JoySockBlockingMode bm)
{
    CJoySockSendData* pData = CJoySockSendData::Create(buf, size);
    bool b = Send(pData, bm);
    pData->Release();

    return b;
}

//##ModelId=44CF175701E8
bool CJoySockData::SendTo(const sockaddr_in& addr, CJoySockSendData* pData)
{
    pData->AddRef();

    CJoySockData::CSendItem* pItem = new CJoySockData::CSendItem;
    pItem->m_next   = NULL;
    pItem->m_data   = pData;
    pItem->m_addr   = addr;

    PostSendItem(pItem);
    return true;
}

//##ModelId=44CF175701F6
bool CJoySockData::SendTo(const sockaddr_in& addr, const void* buf, int size)
{
    CJoySockSendData* pData = CJoySockSendData::Create(buf, size);
    bool b = SendTo(addr, pData);
    pData->Release();

    return b;
}

//##ModelId=44CF175701AB
void CJoySockData::PostClose()
{
    if(m_owner != NULL)
    {
        AddRef();
        g_sockTool.PostAsyMessage(SAM_CLOSE_SOCK, (WPARAM)this, 0);
    }
}

//##ModelId=44CF175701C7
void CJoySockData::Attach(SOCKET s, bool bSend, bool bRecv, bool bBlock)
{
    WSAPROTOCOL_INFO wi;
    int wiLen = sizeof(wi);
    int r = getsockopt(s, SOL_SOCKET, SO_PROTOCOL_INFO, (char*)&wi, &wiLen);
    ASSERT(r == 0);r;
    
    m_hSocket = s;

    m_bStream = (wi.iSocketType==SOCK_STREAM);
    if(m_bStream)
    {
		BOOL temp = TRUE;
		setsockopt(s, SOL_SOCKET, SO_KEEPALIVE, (char*)&temp, sizeof(BOOL));
    }

    CreateThreads(wi.iSocketType, bSend, bRecv, bBlock);
}

//##ModelId=44CF17570204
CJoySockData* CJoySockData::CreateData(
    int type, const sockaddr_in& addr, bool bSend, bool bRecv, bool bBlock)
{
    SOCKET s = socket(AF_INET,
        type, (type == SOCK_STREAM) ? IPPROTO_TCP : IPPROTO_UDP);

    if(s == INVALID_SOCKET)
    {
        TRACE("Create socket error <%d>.\n", WSAGetLastError());
        return NULL;
    }

    if(SOCKET_ERROR == bind(s, (sockaddr*)&addr, sizeof(addr)))
    {
      /*  TRACE("bind address error with socket <%d>, address <%s>, error <%d>.\n", 
            s, (PCSTR)CJoySockBase::AddressToString(addr), WSAGetLastError());*/
        closesocket(s);
        return NULL;
    }

    return CreateData(s, bSend, bRecv, bBlock);
}

//##ModelId=44CF17570215
CJoySockData* CJoySockData::CreateData(
    SOCKET s, bool bSend, bool bRecv, bool bBlock)
{
    CJoySockData* pData = OnCreateData(); //new CJoySockData();
    pData->Attach(s, bSend, bRecv, bBlock);
    return pData;
}

//##ModelId=44CF17570223
CJoySockData* CJoySockData::OnCreateData()
{
    return new CJoySockData();
}

//##ModelId=44CF175702D1
CJoySockData* CJoySockBase::CreateData(SOCKET s, bool bSend, bool bRecv, bool bBlock)
{
    CJoySockData* pData = OnCreateData(); //new CJoySockData();
    pData->Attach(s, bSend, bRecv, bBlock);
    return pData;
}

//##ModelId=44CF175702C5
CJoySockData* CJoySockBase::CreateData(int type, const sockaddr_in& addr, bool bSend, bool bRecv, bool bBlock)
{
    SOCKET s = socket(AF_INET,
        type, (type == SOCK_STREAM) ? IPPROTO_TCP : IPPROTO_UDP);

    if(s == INVALID_SOCKET)
    {
        TRACE("Create socket error <%d>.\n", WSAGetLastError());
        return NULL;
    }

    if(SOCKET_ERROR == bind(s, (sockaddr*)&addr, sizeof(addr)))
    {
      /*  TRACE("bind address error with socket <%d>, address <%s>, error <%d>.\n", 
            s, (PCSTR)CJoySockBase::AddressToString(addr), WSAGetLastError());*/
        closesocket(s);
        return NULL;
    }

    return CreateData(s, bSend, bRecv, bBlock);
}

//##ModelId=44CF175701C5
void CJoySockData::ResumeThreads()
{
    if(m_bStream && !m_bConnected)
    {
        return;
    }

    if(m_hSendThread != NULL)
    {
        AddRef();
        ResumeThread(m_hSendThread);
    }
    if(m_hRecvThread != NULL)
    {
        AddRef();
        ResumeThread(m_hRecvThread);
    }
}

//##ModelId=44CF17570300
void CJoySockBase::SetNoTcpDelay(bool bNoDelay)
{
    if(m_data == NULL)
    {
        return;
    }
    BOOL b = bNoDelay;

    setsockopt(m_data->m_hSocket, IPPROTO_TCP,
        TCP_NODELAY, (const char*)&b, sizeof(b));
    GetTickCount();
}

//##ModelId=44CF175701B6
void CJoySockData::CreateThreads(int type, bool bSend, bool bRecv, bool bBlock)
{
    m_bBlock = bBlock;

    if(bSend)
    {
        setsockopt(m_hSocket, SOL_SOCKET, SO_SNDTIMEO,
            (const char*)&g_sockTool.m_timeOut, sizeof(DWORD));

        m_hSendEvent = CreateEvent(NULL, TRUE, FALSE, NULL);

        LPTHREAD_START_ROUTINE pfn = (type == SOCK_STREAM) ?
            (bBlock ? CJoySocketTool::sSendBlockThreadProc : CJoySocketTool::sSendThreadProc) :
            CJoySocketTool::sSendToThreadProc;
        m_hSendThread = g_sockTool.CreateThread(pfn, this, true);
        //SetThreadPriority(m_hSendThread, THREAD_PRIORITY_TIME_CRITICAL);
        SetThreadPriority(m_hSendThread, g_sockSendThread_Priority);
    }

    if(bRecv)
    {
        setsockopt(m_hSocket, SOL_SOCKET, SO_RCVTIMEO,
            (const char*)&g_sockTool.m_timeOut, sizeof(DWORD));

        LPTHREAD_START_ROUTINE pfn = (type == SOCK_STREAM) ?
            (bBlock ? CJoySocketTool::sRecvBlockThreadProc : CJoySocketTool::sRecvThreadProc) :
            CJoySocketTool::sRecvFromThreadProc;
        m_hRecvThread = g_sockTool.CreateThread(pfn, this, true);
        //SetThreadPriority(m_hRecvThread, THREAD_PRIORITY_TIME_CRITICAL-1);
        SetThreadPriority(m_hRecvThread, g_sockRecvThread_Priority);
    }
}

//##ModelId=44CF17570199
CJoySockData::CSendItem* CJoySockData::PeekSendItem()
{
    CJoyAutoCS gate(m_sendLock);

    if(m_sendList.IsEmpty())
    {
        ResetEvent(m_hSendEvent);
        return NULL;
    }
    return m_sendList.PeekAll();
}

//##ModelId=44CF175701A5
void CJoySockData::PostRecvData(CJoySockRecvData* pData)
{
    g_sockTool.PostRecvData(pData);
}

//##ModelId=44CF175701A8
void CJoySockData::PostSendItem(CSendItem* pItem)
{
    CJoyAutoCS gate(m_sendLock);
    //if(m_sendList.IsEmpty())
    {
        SetEvent(m_hSendEvent);
    }
    m_sendList.AddTail(pItem);
}

// class CJoySockBase

//CString CJoySockBase::AddressToString(const sockaddr_in& addr)
//{
//    CString str;
//    str.Format("%d.%d.%d.%d:%d",
//        addr.sin_addr.S_un.S_un_b.s_b1,
//        addr.sin_addr.S_un.S_un_b.s_b2,
//        addr.sin_addr.S_un.S_un_b.s_b3,
//        addr.sin_addr.S_un.S_un_b.s_b4,
//        ntohs(addr.sin_port));
//    return str;
//}

//##ModelId=44CF175702E4
void CJoySockBase::MakeAddress(sockaddr_in& addr, PCSTR pszIP, int nPort)
{
    memset(&addr, 0, sizeof(addr));

    addr.sin_family = AF_INET;
    addr.sin_port   = htons((WORD)nPort);

    if(pszIP != NULL)
    {
        int b1, b2, b3, b4;
        sscanf(pszIP, "%d.%d.%d.%d", &b1, &b2, &b3, &b4);

        addr.sin_addr.S_un.S_un_b.s_b1  = (BYTE)b1;
        addr.sin_addr.S_un.S_un_b.s_b2  = (BYTE)b2;
        addr.sin_addr.S_un.S_un_b.s_b3  = (BYTE)b3;
        addr.sin_addr.S_un.S_un_b.s_b4  = (BYTE)b4;
    }
}

//##ModelId=44CF17570298
CJoySockBase::CJoySockBase(void)
{
    m_data = NULL;
    m_hConnectThread = NULL;
}

//##ModelId=44CF1757029F
CJoySockBase::~CJoySockBase(void)
{
    Close();
}

//##ModelId=44CF175702A4
void CJoySockBase::SetData(CJoySockData* pData)
{
    pData->AddRef();
    Attach(pData);
}

//##ModelId=44CF175702A2
void CJoySockBase::Attach(CJoySockData* pData)
{
    ASSERT(m_data == NULL && pData != NULL);
    OnAttach(pData);
}

//##ModelId=44CF17570293
void CJoySockBase::OnAttach(CJoySockData* pData)
{
    m_data = pData;
    m_data->m_owner = this;

    m_data->ResumeThreads();
}

//##ModelId=44CF175702A6
bool CJoySockBase::Create(int type, const sockaddr_in& addr, bool bSend, bool bRecv, bool bBlock)
{
    CJoySockData* pData = CreateData(type, addr, bSend, bRecv, bBlock);
    if(pData == NULL)
    {
        return false;
    }

    Attach(pData);
    return true;
}

//##ModelId=44CF175702B3
bool CJoySockBase::Create(int nType, int nPort)
{
    sockaddr_in addr;
    memset(&addr, 0, sizeof(addr));
    addr.sin_family = AF_INET;
    addr.sin_port   = htons((WORD)nPort);

    return Create(nType, addr);
}

//##ModelId=44CF175702C0
bool CJoySockBase::Connect(const sockaddr_in& addr)
{
    ASSERT(m_data != NULL && m_data->m_bStream);

    CJoySockConnect* pConnData = new CJoySockConnect();
    pConnData->m_data = m_data;
    pConnData->m_addr = addr;

    m_data->AddRef();
    m_hConnectThread = g_sockTool.CreateThread(CJoySocketTool::sConnectThreadProc, pConnData);

    return true;
}

//##ModelId=44CF175702C2
bool CJoySockBase::Connect(PCSTR pszIP, int nPort)
{
    sockaddr_in addr;
    MakeAddress(addr, pszIP, nPort);
    return Connect(addr);
}

//##ModelId=44CF175702DE
void CJoySockBase::Close()
{
    if(m_data == NULL)
    {
        return;
    }
    ASSERT(m_data->m_owner == this);

    m_data->m_bClose = true;
    m_data->m_owner = NULL;

    if(m_data->m_hRecvThread != NULL)
    {
        g_sockTool.AddClearThread(m_data->m_hRecvThread);
        m_data->m_hRecvThread = NULL;
    }
    if(m_data->m_hSendThread != NULL)
    {
        m_data->m_sendLock.Lock();
        SetEvent(m_data->m_hSendEvent);
        m_data->m_sendLock.Unlock();

        g_sockTool.AddClearThread(m_data->m_hSendThread);
        m_data->m_hSendThread = NULL;
    }
    if(m_hConnectThread != NULL)
    {
        g_sockTool.AddClearThread(m_hConnectThread);
        m_hConnectThread = NULL;
    }

    m_data->Release();
    m_data = NULL;

    OnClose();
}

//##ModelId=44CF17570296
CJoySockData* CJoySockBase::OnCreateData()
{
    return new CJoySockData();
}

//##ModelId=44CF17570281
void CJoySockBase::OnRecvData(CJoySockRecvData& )
{
}

//##ModelId=44CF17570284
void CJoySockBase::OnClose()
{
}

//#include <winsock2.h>
//##ModelId=44CF17570290
void CJoySockBase::OnConnect(int err)
{
    if(err != 0)
    {
        Close();
        return;
    }
    m_data->ResumeThreads();

    ASSERT(m_hConnectThread != NULL);
    CloseHandle(m_hConnectThread);
    m_hConnectThread = NULL;
}

//##ModelId=44CF175702EE
bool CJoySockBase::Send(CJoySockSendData* pData)
{
    if(m_data == NULL)
    {
        return false;
    }

    return m_data->Send(pData);
}

//##ModelId=44CF175702F0
bool CJoySockBase::Send(const void* buf, int size)
{
    if(m_data == NULL)
    {
        return false;
    }

    return m_data->Send(buf, size);
}

//##ModelId=44CF175702F3
bool CJoySockBase::SendTo(const sockaddr_in& addr, CJoySockSendData* pData)
{
    if(m_data == NULL)
    {
        return false;
    }

    return m_data->SendTo(addr, pData);
}

//##ModelId=44CF175702F6
bool CJoySockBase::SendTo(const sockaddr_in& addr, const void* buf, int size)
{
    if(m_data == NULL)
    {
        return false;
    }

    return m_data->SendTo(addr, buf, size);
}
